import sys
import json
import os
import pickle
import requests
from collections import namedtuple
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton,
                             QListWidget, QFileDialog, QMessageBox, QComboBox, QProgressDialog)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from tqdm import tqdm

MusicEntry = namedtuple('MusicEntry', ['author', 'title', 'url'])
CONFIG_FILE = 'config.json'
DEFAULT_DOWNLOAD_PATH = os.getcwd()
HEADERS = {
    'accept': 'application/json, text/javascript, */*; q=0.01',
    'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ru;q=0.5',
    'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'origin': 'https://music.txqq.pro',
    'referer': 'https://music.txqq.pro/?name=%E6%99%B4%E5%A4%A9&type=qq',
    'sec-ch-ua': '"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'same-origin',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0',
    'x-requested-with': 'XMLHttpRequest',
}
DATA_SOURCES_MAP = {
    'qq': '腾讯QQ音乐',
    'kugou': '酷狗音乐',
    'kuwo': '酷我音乐',
    'baidu': '千千音乐',
    '1ting': '一听音乐',
    'migu': '咪咕音乐',
    'lizhi': '荔枝FM',
    'qingting': '蜻蜓FM',
    'ximalaya': '喜马拉雅',
    '5singyc': '5sing原创',
    '5sing_cover': '5sing翻唱',
    'kg': '全民K歌',
}

class AppState:
    def __init__(self, first_time=True):
        self.first_time = first_time

    def save(self, file_path="app_state.pickle"):
        with open(file_path, "wb") as f:
            pickle.dump(self, f)

    @staticmethod
    def load(file_path="app_state.pickle"):
        try:
            with open(file_path, "rb") as f:
                return pickle.load(f)
        except FileNotFoundError:
            return AppState()

def ensure_config_file_exists():
    if not os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'w') as file:
            json.dump({'download_path': DEFAULT_DOWNLOAD_PATH}, file, indent=4)

def load_download_path():
    try:
        with open(CONFIG_FILE, 'r') as file:
            config = json.load(file)
            path = config.get('download_path', DEFAULT_DOWNLOAD_PATH)
            return os.path.abspath(path)
    except FileNotFoundError:
        ensure_config_file_exists()
        return DEFAULT_DOWNLOAD_PATH

def update_download_path(new_path):
    with open(CONFIG_FILE, 'w') as file:
        json.dump({'download_path': os.path.abspath(new_path)}, file, indent=4)

def download(url, name, path):
    res = requests.get(url, stream=True)
    music_content = res.content
    data_size = round(int(res.headers.get('Content-Length', 0)) / 1024 / 1024)
    with open(os.path.join(path, f"{name}.mp3"), 'wb') as f:
        for data in tqdm(iterable=res.iter_content(1024 * 1024), total=data_size, desc='正在下载', unit='MB'):
            f.write(data)

class SearchMusicThread(QThread):
    result_ready = pyqtSignal(list)

    def __init__(self, name, source):
        super().__init__()
        self.name = name
        self.source = source

    def run(self):
        music_list = []
        for i in range(1, 10):
            data = {
                'input': self.name,
                'filter': 'name',
                'type': self.source,
                'page': i,
            }
            res = requests.post('https://music.txqq.pro/', headers=HEADERS, data=data)
            datas = json.loads(res.text)
            data_list = datas.get('data', [])
            for item in data_list:
                author = item.get('author', '未知')
                title = item.get('title', '未知')
                url = item.get('url', '')
                if url:
                    music_list.append(MusicEntry(author, title, url))
        self.result_ready.emit(music_list)

class MusicDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.download_path = load_download_path()
        self.current_music_source = 'qq'
        self.music_list = []

    def init_ui(self):
        self.setWindowTitle('音乐下载器')
        self.setGeometry(100, 100, 600, 400)

        layout = QVBoxLayout()

        self.label = QLabel('请输入音乐名称:')
        self.music_input = QLineEdit()
        self.search_button = QPushButton('搜索')
        self.search_button.clicked.connect(self.search_music)

        self.music_source_combo = QComboBox()
        self.music_source_combo.addItems(DATA_SOURCES_MAP.values())
        self.music_source_combo.currentIndexChanged.connect(self.change_music_source)

        self.music_list_widget = QListWidget()
        self.music_list_widget.itemDoubleClicked.connect(self.download_music)

        self.path_button = QPushButton('设置下载路径')
        self.path_button.clicked.connect(self.set_download_path)

        layout.addWidget(self.label)
        layout.addWidget(self.music_input)
        layout.addWidget(self.music_source_combo)
        layout.addWidget(self.search_button)
        layout.addWidget(self.music_list_widget)
        layout.addWidget(self.path_button)

        self.setLayout(layout)

    def change_music_source(self, index):
        self.current_music_source = list(DATA_SOURCES_MAP.keys())[index]

    def search_music(self):
        music_name = self.music_input.text()
        self.music_list_widget.clear()
        self.progress_dialog = QProgressDialog("正在搜索...", "取消", 0, 0, self)
        self.progress_dialog.setWindowTitle("请稍候")
        self.progress_dialog.setWindowModality(Qt.WindowModal)
        self.progress_dialog.show()

        self.search_thread = SearchMusicThread(music_name, self.current_music_source)
        self.search_thread.result_ready.connect(self.display_search_results)
        self.search_thread.start()

    def display_search_results(self, music_list):
        self.progress_dialog.close()
        self.music_list = music_list
        if not self.music_list:
            self.music_list_widget.addItem('没有找到相关信息')
        else:
            for entry in self.music_list:
                self.music_list_widget.addItem(f"{entry.title} - {entry.author}")

    def download_music(self, item):
        selected_index = self.music_list_widget.row(item)
        selected_entry = self.music_list[selected_index]
        download(selected_entry.url, selected_entry.title, self.download_path)
        QMessageBox.information(self, '提示', f'{selected_entry.title} 下载成功！')

    def set_download_path(self):
        path = QFileDialog.getExistingDirectory(self, '选择下载路径', self.download_path)
        if path:
            self.download_path = path
            update_download_path(self.download_path)
            QMessageBox.information(self, '提示', f'下载路径已更新为: {self.download_path}')

def main():
    app = QApplication(sys.argv)
    window = MusicDownloader()
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
